package com.mye.prometheusexportdcim.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import com.mye.prometheusexportdcim.basemodel.HttpResponseTemp;
import com.mye.prometheusexportdcim.configuration.HttpClient;
import com.mye.prometheusexportdcim.model.common.ClientConfigureNew;
import com.mye.prometheusexportdcim.model.common.GetSnmpInfoTask;
import com.mye.prometheusexportdcim.model.exporter.ExporterEnv;
import com.mye.prometheusexportdcim.model.gaugeMetric.GaugeMetric;
import com.mye.prometheusexportdcim.model.gaugeMetric.Snmp;
import com.mye.prometheusexportdcim.model.gaugeMetric.SnmpInfo;
import com.mye.prometheusexportdcim.service.MetricsService;
import com.mye.prometheusexportdcim.utils.LogLocalityUtil;
import com.mye.prometheusexportdcim.utils.TextFormat;
import io.prometheus.client.Collector;
import io.prometheus.client.GaugeMetricFamily;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.snmp4j.mp.SnmpConstants;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.StringWriter;
import java.io.Writer;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author hch
 * @创建人 hch
 * @创建时间 2021/6/28 16:00
 * @描述 TODO
 * @版本 1.0.0
 */
@Slf4j
@Service
public class MetricsServiceImpl implements MetricsService {


    @Resource
    HttpClient httpClient;

    @Resource
    ExporterEnv exporterEnv;

    @Override
    public ResponseEntity<String> metrics() {

        List<GaugeMetric> gaugeMetricList = checkParameter();


//        String s1 = JSONUtil.toJsonStr(gaugeMetricList);
//        log.info("gaugeMetricList: " + s1);

        if (CollectionUtil.isEmpty(gaugeMetricList)) {
            return getEmptyResponseEntity();
        }
        List<Snmp> snmpList = gaugeMetricList.stream().filter(Objects::nonNull).map(GaugeMetric::getSnmp).filter(Objects::nonNull).collect(Collectors.toList());

        if (CollectionUtil.isEmpty(snmpList)) {
            log.error(LogLocalityUtil.getLogLocality() + " 错误信息:snmp信息集合为空");
            return getEmptyResponseEntity();
        }

        List<GetSnmpInfoTask> taskList = convertOfSnmpTask(snmpList);
        if (CollectionUtil.isEmpty(taskList)) {
            log.error(LogLocalityUtil.getLogLocality() + " 错误信息:snmp信息集合为空2");
            return getEmptyResponseEntity();
        }
        // TimeInterval timer = DateUtil.timer();
        List<List<SnmpInfo>> lists;
        try {
            lists = ClientConfigureNew.executeCompletionService(taskList);
        } catch (Exception e) {
            log.error(LogLocalityUtil.getLogLocality() + " 错误信息:获取snmp信息发生错误: " + e);
            return getEmptyResponseEntity();
        }
        List<SnmpInfo> snmpInfoList = new ArrayList<>();
        if (CollectionUtil.isEmpty(lists)) {
            return getEmptyResponseEntity();
        }
        lists.forEach(snmpInfoList::addAll);
        Map<String, Map<String, List<Double>>> collectMap = snmpInfoList.stream().filter(Objects::nonNull).collect(Collectors.groupingBy(SnmpInfo::getSnmpAddress, Collectors.groupingBy(SnmpInfo::getOid, Collectors.mapping(SnmpInfo::getValue, Collectors.toList()))));
        // log.info("获取snmp数据花费时间1: " + timer.interval());
        if (CollectionUtil.isEmpty(collectMap)) {
            log.error(LogLocalityUtil.getLogLocality() + " 错误信息:snmp指标值集合为空3");
            return getEmptyResponseEntity();
        }
        List<Collector.MetricFamilySamples> mfs = new ArrayList<>();
        Map<String, List<GaugeMetric>> gaugeMetricListMap = gaugeMetricList.stream().filter(Objects::nonNull).collect(Collectors.groupingBy(GaugeMetric::getNameEn));
        // log.info(LogLocalityUtil.getLogLocality() + " gaugeMetricListMap："+gaugeMetricListMap.keySet().size());
        gaugeMetricListMap.forEach((key, value) -> convertOfGaugeMetric2(key, value, collectMap, mfs));
        // log.info("获取snmp数据花费时间2: " + timer.interval());

        if (CollectionUtil.isEmpty(mfs)) {
            return getEmptyResponseEntity();
        }
        // log.info(LogLocalityUtil.getLogLocality() + " mfs大小："+mfs.size());
        String result = goWrite(mfs);
        // log.info("result=== " + "\r\n" + result);
        if (StringUtils.isBlank(result)) {
            return getEmptyResponseEntity();
        } else {
            return getResponseEntity(result);
        }
    }


    private ResponseEntity<String> getResponseEntity(String result) {
        return ResponseEntity.ok().header("Content-Type", TextFormat.CONTENT_TYPE_004).body(result);
    }


    private ResponseEntity<String> getEmptyResponseEntity() {
        return ResponseEntity.ok().header("Content-Type", TextFormat.CONTENT_TYPE_004).body("#EOF");
    }


    private String goWrite(List<Collector.MetricFamilySamples> mfs) {
        Writer writer = new StringWriter();
        String result = null;
        try {
            TextFormat.write004(writer, mfs);
            writer.flush();
            result = writer.toString();
        } catch (Exception e) {
            log.error(LogLocalityUtil.getLogLocality() + " write文件流发生错误: " + e);
        } finally {
            try {
                writer.close();
            } catch (Exception e) {
                log.error(LogLocalityUtil.getLogLocality() + " write文件流关闭时发生错误: " + e);
            }
        }
        return result;
    }

    private List<GetSnmpInfoTask> convertOfSnmpTask(List<Snmp> snmpList) {

        Map<String, Map<Integer, List<Snmp>>> collect = snmpList.stream().filter(Objects::nonNull).collect(Collectors.groupingBy(Snmp::getSnmpAddress, Collectors.groupingBy(Snmp::getSnmpProtocol)));
        if (CollectionUtil.isEmpty(collect)) {
            return null;
        }
        List<GetSnmpInfoTask> taskList = new ArrayList<>();
        collect.forEach((key, value) -> {
            GetSnmpInfoTask snmpInfoRoot = new GetSnmpInfoTask();
            snmpInfoRoot.setSnmpAddress(key);
            value.forEach((key2, value2) -> {
                GetSnmpInfoTask snmpInfo = snmpInfoRoot.clone();
                snmpInfo.setSnmpProtocol(key2);
                List<String> oidList = value2.stream().filter(Objects::nonNull).map(Snmp::getOid).distinct().collect(Collectors.toList());
                if (CollectionUtil.isEmpty(oidList)) {
                    return;
                }
                snmpInfo.setOids(oidList);
                if (SnmpConstants.version3 == key2) {
                    snmpInfo.setUsername(value2.get(0).getUsername());
                    snmpInfo.setAuthWay(value2.get(0).getAuthWay());
                    snmpInfo.setAuthPassword(value2.get(0).getAuthPassword());
                    snmpInfo.setEncryptionWay(value2.get(0).getEncryptionWay());
                    snmpInfo.setEncryptionPassword(value2.get(0).getEncryptionPassword());
                }
                taskList.add(snmpInfo);
            });
        });
        return taskList;
    }


    private List<GaugeMetric> checkParameter() {

        int exporterId = exporterEnv.getExporterId();

        String dcimCollectUrl = exporterEnv.getDcimCollectUrl();


        String url = StringUtils.join(dcimCollectUrl, exporterId);
        List<GaugeMetric> result = ListUtil.empty();
        try {
            HttpResponseTemp<List<GaugeMetric>> responseTemp = httpClient.getMetrics(url);
            if (ObjectUtil.isNotNull(responseTemp)) {
                int resultCode = responseTemp.getResultCode();
                String resultMsg = responseTemp.getResultMsg();
                result = responseTemp.getResult();

                //仅供本地测试，线上去掉这块
//                String ipAddr = "172.100.40.41";
//                String snmpAddress = "udp:172.100.40.41/161";
//                result = result.stream().filter(Objects::nonNull).peek(s -> {
//                    Snmp snmp1 = s.getSnmp();
//                    snmp1.setIpAddr(ipAddr);
//                    snmp1.setSnmpAddress(snmpAddress);
//                    s.setSnmp(snmp1);
//                }).collect(Collectors.toList());
                log.info(LogLocalityUtil.getLogLocality() + " resultCode: {},resultMsg: {},result.size: {}", resultCode, resultMsg, result.size());
            }

        } catch (Exception e) {

            log.error(LogLocalityUtil.getLogLocality() + " 通过http请求获取该exporter采集指标信息发送错误: {}", e);

        }


        return result;


    }


    private void convertOfGaugeMetric2(String key, List<GaugeMetric> list, Map<String, Map<String, List<Double>>> map, List<Collector.MetricFamilySamples> mfs) {
        if (CollectionUtil.isEmpty(list)) {
            return;
        }
        GaugeMetric gaugeMetric = list.get(0);
        if (gaugeMetric == null) {
            return;
        }

        String help = gaugeMetric.getHelp();
        GaugeMetricFamily labeledGauge = new GaugeMetricFamily(key, help, Arrays.asList("nameChs", "description", "signalCollectId", "componentId", "componentInstanceId", "dynamicId", "componentSignalId", "hostGroupId", "snmpAddress", "oid", "threshold"));

        list.stream().filter(Objects::nonNull).forEach(s -> {
            Snmp snmp = s.getSnmp();
            if (snmp == null) {
                return;
            }
            String snmpAddress = snmp.getSnmpAddress();
            String oid = snmp.getOid();
            double value = 0.0D;
            if (map.containsKey(snmpAddress)) {
                Map<String, List<Double>> stringListMap = map.get(snmpAddress);
                if (CollectionUtil.isNotEmpty(stringListMap) && stringListMap.containsKey(oid)) {
                    List<Double> doubles = stringListMap.get(oid);
                    if (CollectionUtil.isNotEmpty(doubles)) {
                        Double aDouble = doubles.get(0);
                        if (aDouble == null) {
                            return;
                        }
                        value = aDouble;
                    }
                }
            }
            labeledGauge.addMetric(Arrays.asList(s.getNameChs(), s.getDescription(), s.getSignalCollectId() + "", s.getComponentId() + "", s.getComponentInstanceId() + "", s.getDynamicId() + "", s.getComponentSignalId() + "", s.getHostGroupId() + "", s.getSnmp().getSnmpAddress(), s.getSnmp().getOid(), "0"), value);
        });

        mfs.add(labeledGauge);


    }


    @Override
    public ResponseEntity<String> heartbeat() {
        return ResponseEntity.ok().header("Content-Type", TextFormat.CONTENT_TYPE_004).body("pong");
    }
}
